package com.swak.reactivex.threads;

import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

import com.swak.metrics.MetricsFactory;
import com.swak.metrics.PoolMetrics;

/**
 * 顺序执行
 * 
 * @author lifeng
 * @date 2020年8月20日 下午9:52:03
 */
@SuppressWarnings({ "rawtypes", "unchecked" })
public class OrderContext extends ThreadPoolExecutor implements Context {

	private volatile PoolMetrics metrics;
	private String name;
	private int nThreads;

	/**
	 * 创建执行需时任务的线程池
	 *
	 * @param prefix          线程名称前缀
	 * @param nThreads        核心线程数
	 * @param daemon          是否后台线程
	 * @param maxExecTime     最大执行时间
	 * @param maxExecTimeUnit 最大执行时间类型
	 * @return 线程上下文
	 */
	public OrderContext(String prefix, int nThreads, boolean daemon, BlockedThreadChecker checker, long maxExecTime,
			TimeUnit maxExecTimeUnit) {
		super(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>(),
				new SwakThreadFactory(prefix, daemon, new AtomicInteger(0), checker, maxExecTime, maxExecTimeUnit));
		this.name = prefix;
		this.nThreads = nThreads;
	}

	@Override
	public void applyMetrics(MetricsFactory metricsFactory) {
		this.metrics = metricsFactory.createPoolMetrics(name, nThreads);
	}

	@Override
	public PoolMetrics<Object> getMetrics() {
		return this.metrics;
	}

	/**
	 * 添加指标监控 -- 不需要记录当前线程的Context
	 */
	@Override
	public void execute(Runnable command) {
		super.execute(command);
	}

	/**
	 * 创建顺序的执行器，并发度为1
	 * 
	 * @return
	 */
	public OrderAbleContext newOrderAbleContext() {
		return new OrderAbleContext(this, 1);
	}

	/**
	 * 创建顺序的执行器，指定并发度
	 * 
	 * @return
	 */
	public OrderAbleContext newOrderAbleContext(int multi) {
		return new OrderAbleContext(this, multi);
	}

	/**
	 * 创建顺序执行器
	 * 
	 * @author DELL
	 */
	static public class OrderAbleContext implements Context {

		private Context innerContext;
		private final int multi;
		private final Queue orderTasks;

		public OrderAbleContext(Context innerContext, int multi) {
			this.innerContext = innerContext;
			this.multi = multi;
			this.orderTasks = this.createQueue();
		}

		private Queue createQueue() {
			return this.multi <= 1 ? new TaskQueue() : new MultiTaskQueue(this.multi);
		}

		@Override
		public void execute(Runnable command) {
			orderTasks.execute(() -> {
				this.executeTask(command);
			}, innerContext);
		}

		@Override
		public PoolMetrics<Object> getMetrics() {
			return innerContext.getMetrics();
		}

		@Override
		public void runOnContext(Runnable command) {
			this.execute(command);
		}

		public void shutdown() {
			orderTasks.shutdown();
		}

		public void shutdownNow() {
			orderTasks.shutdownNow();
		}
	}
}